﻿// Copyright 2007-2009 Stuart Caborn, Alex Scordellis
//  
//  Licensed under the Apache License, Version 2.0 (the "License"); you may not
//  use this file except in compliance with the License. You may obtain a copy of
//  the License at
//  
//  http://www.apache.org/licenses/LICENSE-2.0
//  
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
//  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
//  License for the specific language governing permissions and limitations under
//  the License.
// 


using System.Linq;
using NUnit.Framework;
using NUnit.Framework.SyntaxHelpers;
using Rhino.Mocks;
using SampleApp.Monitored;
using SampleApp.MonitoredGenerics;

namespace NSynthesis.Test.Wired
{
    [TestFixture]
    public class MethodRecorderTest_Generics
    {
        // Generic interfaces/classes mentioned in these tests fall into two namespaces:
        // - SampleApp.Monitored
        //      These ARE NOT monitored by NSynthesis because the Watch attribute applied to *.Monitored.*
        //      does not specify that generics should be monitored.
        // - SampleApp.MonitoredGenerics
        //      These ARE monitored by NSynthesis because the Watch attribute applied to *.MonitoredGenerics.*
        //      does specify that generics should be monitored
        // The relevant Watch attributes are in this assembly's AssemblyInfo.cs.

        #region Setup/Teardown

        [SetUp]
        public void SetUp()
        {
            Synthesis.Reset();
            mocks = new MockRepository();
        }

        #endregion

        private MockRepository mocks;

        [Test]
        public void generic_methods_are_ignored_by_default()
        {
            var mockedMonitoredClass = mocks.CreateMock<IMonitoredClass>();

            mockedMonitoredClass.GenericMethodA<string>();
            new MonitoredImplementationA().GenericMethodA<string>();

            Assert.That(Synthesis.Recorder.MissingMethods.Count, Is.EqualTo(0));
            Assert.That(Synthesis.Recorder.MockedMethodCalls.Count(), Is.EqualTo(0));
            Assert.That(Synthesis.Recorder.RealMethodCalls.Count(), Is.EqualTo(0));
        }

        [Test]
        public void generic_methods_on_generic_interfaces_are_ignored_by_default()
        {
            var mockedMonitoredClass = mocks.CreateMock<IGenericInterface<string>>();

            mockedMonitoredClass.Load();
            new GenericImplementation<int>().Load();

            Assert.That(Synthesis.Recorder.MissingMethods.Count, Is.EqualTo(0));
            Assert.That(Synthesis.Recorder.MockedMethodCalls.Count(), Is.EqualTo(0));
            Assert.That(Synthesis.Recorder.RealMethodCalls.Count(), Is.EqualTo(0));
        }

        [Test]
        public void regular_methods_on_generic_interfaces_are_ignored_by_default()
        {
            var mockedMonitoredClass = mocks.CreateMock<IGenericInterface<string>>();

            mockedMonitoredClass.Describe();
            new GenericImplementation<int>().Describe();

            Assert.That(Synthesis.Recorder.MissingMethods.Count, Is.EqualTo(0));
            Assert.That(Synthesis.Recorder.MockedMethodCalls.Count(), Is.EqualTo(0));
            Assert.That(Synthesis.Recorder.RealMethodCalls.Count(), Is.EqualTo(0));
        }

        [Test]
        public void generic_methods_on_generic_interfaces_are_monitored_when_specified_in_watch_attribute()
        {
            // IRepository<T> is in the namespace SampleApp.MonitoredGenerics, so should get
            // picked up by the second WatchAttribute in NSynthesis.Test's AssemblyInfo.cs, on which
            // the MonitorGenerics attribute is set to true
            mocks.CreateMock<IRepository<string>>().Load(1);

            Assert.That(Synthesis.Recorder.MockedMethodCalls.Count(), Is.EqualTo(1));
        }

        [Test]
        public void production_implementations_of_generic_methods_on_generic_interfaces_are_monitored_when_specified_in_watch_attribute()
        {
            // IRepository<T> is in the namespace SampleApp.MonitoredGenerics, so should get
            // picked up by the second WatchAttribute in NSynthesis.Test's AssemblyInfo.cs, on which
            // the MonitorGenerics attribute is set to true
            new GenericConcreteRepository<string>().Load(3);
            Assert.That(Synthesis.Recorder.RealMethodCalls.Count(), Is.EqualTo(1));
        }

        [Test]
        public void generic_methods_on_regular_classes_are_monitored_when_specified_in_watch_attribute()
        {
            // ClassWithGenericMethods is in the namespace SampleApp.MonitoredGenerics, so should get
            // picked up by the second WatchAttribute in NSynthesis.Test's AssemblyInfo.cs, on which
            // the MonitorGenerics attribute is set to true
            mocks.CreateMock<ClassWithGenericMethods>().Foo(42);

            Assert.That(Synthesis.Recorder.MockedMethodCalls.Count(), Is.EqualTo(1));
        }

        [Test]
        public void production_implementations_of_generic_methods_on_regular_classes_are_monitored_when_specified_in_watch_attribute()
        {
            // IRepository<T> is in the namespace SampleApp.MonitoredGenerics, so should get
            // picked up by the second WatchAttribute in NSynthesis.Test's AssemblyInfo.cs, on which
            // the MonitorGenerics attribute is set to true
            new ClassWithGenericMethods().Foo(123);
            Assert.That(Synthesis.Recorder.RealMethodCalls.Count(), Is.EqualTo(1));
        }

        [Test]
        public void generic_methods_on_regular_interfaces_are_monitored_when_specified_in_watch_attribute()
        {
            // InterfaceWithGenericMethods is in the namespace SampleApp.MonitoredGenerics, so should get
            // picked up by the second WatchAttribute in NSynthesis.Test's AssemblyInfo.cs, on which
            // the MonitorGenerics attribute is set to true
            var mockedClassWithGenericMethods = mocks.CreateMock<InterfaceWithGenericMethods>();
            mockedClassWithGenericMethods.Foo(42);

            Assert.That(Synthesis.Recorder.MockedMethodCalls.Count(), Is.EqualTo(1));
        }

        [Test]
        public void production_implementations_of_generic_methods_on_regular_interfaces_are_monitored_when_specified_in_watch_attribute()
        {
            // InterfaceWithGenericMethods is in the namespace SampleApp.MonitoredGenerics, so should get
            // picked up by the second WatchAttribute in NSynthesis.Test's AssemblyInfo.cs, on which
            // the MonitorGenerics attribute is set to true
            new ImplementsInterfaceWithGenericMethods().Foo(42);
            Assert.That(Synthesis.Recorder.RealMethodCalls.Count(), Is.EqualTo(1));
        }

        [Test]
        public void regular_methods_on_generic_interfaces_are_monitored_when_specified_in_watch_attribute()
        {
            // IRepository<T> is in the namespace SampleApp.MonitoredGenerics, so should get
            // picked up by the second WatchAttribute in NSynthesis.Test's AssemblyInfo.cs, on which
            // the MonitorGenerics attribute is set to true
            mocks.CreateMock<IRepository<string>>().Count();

            Assert.That(Synthesis.Recorder.MockedMethodCalls.Count(), Is.EqualTo(1));
        }

        [Test]
        public void production_implementations_of_regular_methods_on_generic_interfaces_are_monitored_when_specified_in_watch_attribute()
        {
            // IRepository<T> is in the namespace SampleApp.MonitoredGenerics, so should get
            // picked up by the second WatchAttribute in NSynthesis.Test's AssemblyInfo.cs, on which
            // the MonitorGenerics attribute is set to true
            new GenericConcreteRepository<string>().Count();

            Assert.That(Synthesis.Recorder.RealMethodCalls.Count(), Is.EqualTo(1));
        }

        [Test]
        public void a_mock_call_to_a_generic_interface_passes_when_all_implementations_are_invoked()
        {
            mocks.CreateMock<IRepository<Customer>>().Load(4567);

            new CustomerRepository().Load(1);
            new GenericConcreteRepository<Customer>().Load(2);

            Assert.That(Synthesis.Recorder.MissingMethods.Count, Is.EqualTo(0));
        }

        [Test]
        public void a_mock_call_to_a_generic_interface_does_not_pass_when_not_all_implementations_are_invoked()
        {
            mocks.CreateMock<IRepository<Customer>>().Load(4567);

            new CustomerRepository().Load(1);

            // Forgot to test:
            // new GenericConcreteRepository<Customer>().Load(2);

            Assert.That(Synthesis.Recorder.MissingMethods.Count, Is.EqualTo(1));
        }

        [Test]
        public void a_mock_call_to_a_generic_interface_passes_when_open_implementations_are_invoked_with_different_type_params()
        {
            mocks.CreateMock<IRepository<Customer>>().Load(4567);

            new CustomerRepository().Load(1);

            // Counts as a test of GenericConcreteRepository<Customer>.Load().
            new GenericConcreteRepository<Widget>().Load(2);

            Assert.That(Synthesis.Recorder.MissingMethods.Count, Is.EqualTo(0));
        }

        [Test]
        public void a_mock_call_to_a_generic_interface_does_not_pass_when_closed_implementations_with_different_type_params_are_invoked()
        {
            mocks.CreateMock<IRepository<Customer>>().Load(4567);

            new CustomerRepository().Load(1);
            new WidgetRepository().Load(2);

            // Did not test GenericConcreteRepository<Widget>()

            Assert.That(Synthesis.Recorder.MissingMethods.Count, Is.EqualTo(1));
        }
    }
}